using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Design;
using Needle.DevToolkit.Traits;
using System.Reflection;
using System.Linq.Expressions;

namespace Needle.DevToolkit.Services.UI
{
    [AttributeUsage(AttributeTargets.Method)]
    public class VerbAttribute : Attribute
    {

        public static ItemVerb[] GetVerbs(object instance)
        {
            if (instance == null)
                throw new ArgumentNullException("instance", "instance is null.");
            List<ItemVerb> verbs = new List<ItemVerb>();
            foreach (MethodInfo method in instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) 
            {
                if (method.IsDefined(typeof(VerbAttribute), false))
                {
                    VerbAttribute[] verbAttrs = (VerbAttribute[])Attribute.GetCustomAttributes(method, typeof(VerbAttribute));
                    foreach (var verbAttr in verbAttrs)
                    {
                        ItemVerb verb = verbAttr.CreateVerb(instance, method);
                        verbs.Add(verb);
                    }
                }
            }
            return verbs.ToArray();
        }


        public VerbAttribute()
        {
        }

        public VerbAttribute(string menuGroup, int commandId, string text)
        {
            this.Id = new CommandID(new Guid(menuGroup), commandId);
            this.Text = text;
        }

        public string Text { get; set; }
        public CommandID Id { get; set; }
        public Type RequiredTrait { get; set; } 

        public ItemVerb CreateVerb(object instance, MethodInfo method)
        {
            ParameterExpression verbParam = Expression.Parameter(typeof(ItemVerb), "verb");
            ParameterExpression itemParam = Expression.Parameter(typeof(IItem), "item");

            ConstantExpression instanceExp = LambdaExpression.Constant(instance);
            MethodCallExpression methodCallExp = LambdaExpression.Call(instanceExp, method, verbParam, itemParam);
            Expression<Action<ItemVerb, IItem>> handlerExp = Expression.Lambda<Action<ItemVerb, IItem>>(methodCallExp, verbParam, itemParam);
            Action<ItemVerb, IItem> handler = handlerExp.Compile();

            return new ItemVerb(Text, handler, Id, RequiredTrait);
        }
    }
}
